package ubadb.bench.tests;

import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;

import ubadb.bench.BufferPoolTestCase;
import ubadb.bench.util.BufferManagerCountImpl;
import ubadb.bench.util.TraceUtil;
import ubadb.core.common.TableId;
import ubadb.core.components.bufferManager.BufferManager;
import ubadb.core.components.bufferManager.bufferPool.BufferPool;
import ubadb.external.bufferManagement.etc.FaultCounterDiskManagerSpy;
import ubadb.external.bufferManagement.etc.PageReference;
import ubadb.external.bufferManagement.etc.PageReferenceTrace;

public class Test {

	private static final long PAUSE_BETWEEN_REFERENCES = 1;

	private final String name;
	private final PageReferenceTrace trace;

	public Test(String name, PageReferenceTrace trace) {
		super();
		this.name = name;
		this.trace = trace;
	}

	public String getName() {
		return name;
	}

	public TestResult test(BufferPoolTestCase bufferCase, Integer bufferSize) {
		BufferPool bufferPool = bufferCase.createBufferPool(bufferSize, this);
		if (bufferPool == null)
			return null;
		FaultCounterDiskManagerSpy diskManager = new FaultCounterDiskManagerSpy();
		BufferManagerCountImpl bufferManager = new BufferManagerCountImpl(
				diskManager, bufferPool);

		try {
			simulateTraces(bufferManager, trace, bufferCase);
			int totalReads = bufferManager.getReadsCount();
			int diskReads = diskManager.getFaultsCount();
			return new TestResult(totalReads, diskReads);
		} catch (RuntimeException e) {
			return new TestResult(e);
		}

	}

	private void simulateTraces(BufferManager bufferManager,
			PageReferenceTrace trace, BufferPoolTestCase testCase) {
		try {
			for (PageReference pageReference : trace.getPageReferences()) {
				switch (pageReference.getType()) {
				case REQUEST: {
					bufferManager.readPage(pageReference.getPageId());
					break;
				}
				case RELEASE: {
					bufferManager.releasePage(pageReference.getPageId());
					break;
				}
				}
				testCase.executedRequest();
				Thread.sleep(PAUSE_BETWEEN_REFERENCES);
			}
		} catch (Exception e) {
			throw new RuntimeException("Error running test: " + e.getMessage(),
					e);
		}

	}

	public PageReferenceTrace getTrace() {
		return trace;
	}

	public TreeSet<Integer> getBuffersSizes(int count) {
		int minBufferSize = TraceUtil.calculateMaxPinned(getTrace(), null);
		int maxBufferSize = TraceUtil
				.calculateNumberOfDifferentPages(getTrace());
		Set<Integer> bufferSizes = new HashSet<Integer>();
		bufferSizes.add(minBufferSize);
		bufferSizes.add(maxBufferSize);
		int delta = Math.max(1, (maxBufferSize - minBufferSize) / count);
		for (int b = minBufferSize; b < maxBufferSize; b += delta) {
			bufferSizes.add(b);
		}
		return new TreeSet<Integer>(bufferSizes);
	}

	public int getMinBuffersFor(TableId tableId) {
		return TraceUtil.calculateMaxPinned(getTrace(), tableId);
	}

	public Set<TableId> getTables() {
		Set<TableId> result = new HashSet<TableId>();
		for (PageReference r : trace.getPageReferences()) {
			result.add(r.getPageId().getTableId());
		}
		return result;
	}

	public int getTableSize(TableId tableId) {
		Set<Integer> result = new HashSet<Integer>();
		for (PageReference r : trace.getPageReferences()) {
			if (tableId == null || tableId.equals(r.getPageId().getTableId())) {
				result.add(r.getPageId().getNumber());
			}
		}
		return result.size();
	}

}
